#region COPYRIGHT
//--------------------------------------------------------------------------------
// <copyright file="modulebase.cs" company="starLiGHT Entertainment Studios">
//    Copyright (c) 2007, 2008, 2009, 2010, 2011
//       Roland Rosenkranz (Glatzemann@email.de)
//
//    Based on libnoise by Jason Bevins
//      Copyright (C) 2003, 2004 Jason Bevins (licensed under LGPL)
// </copyright>
// <license>
//   This file is part of starLiGHT.Noise.
//
//   starLiGHT.Noise is free software: you can redistribute it and/or modify
//   it under the terms of the GNU Lesser General Public License as published by
//   the Free Software Foundation, either version 3 of the License, or
//   (at your option) any later version.
//
//   starLiGHT.Noise is distributed in the hope that it will be useful,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   GNU Lesser General Public License for more details.
//
//   You should have received a copy of the GNU Lesser General Public License
//   along with starLiGHT.Noise. If not, see http://www.gnu.org/licenses/.
//    
//   ADDITIONAL (commercial) LICENSES for starLiGHT.Noise are available on request.
// </license>
// <version>
// **************[ starLiGHT.Engine SVN ]**********************
// * $Rev:: 10370                 $:  Revision of last commit *
// * $Author:: unknown            $:  Author of last commit   *
// * $Date:: 2011-06-12 12:29:10 #$:  Date of last commit     *
// ************************************************************
// </version>
//--------------------------------------------------------------------------------
#endregion

namespace starLiGHT.Noise.Noise
{
    #region Using Statements
    using System;
    using System.Collections.Generic;
    using System.Text;

    #endregion

    public class Module
    {
        protected Module[] sourceModule;
        protected bool[] cacheSource;
        protected bool[] resetSourceCache;
        protected double[,,] sourceCache;

        private int sourceModuleCount;

        public Module(int sourceModuleCount)
        {
            this.sourceModuleCount = sourceModuleCount;
            this.sourceModule = new Module[this.sourceModuleCount];

            this.cacheSource = new bool[sourceModuleCount];
            this.sourceCache = new double[sourceModuleCount, 255, 255];

            this.resetSourceCache = new bool[sourceModuleCount];
            // all caches are empty -> need to be filled = resetted
            for (int i = 0; i < sourceModuleCount; i++)
            {
                this.resetSourceCache[i] = true;
            }
        }

        public virtual List<Parameter> ParameterList
        {
            get { return new List<Parameter>(); }
        }

        public virtual Module GetSourceModule(int index)
        {
            if (index >= this.GetSourceModuleCount() || index < 0 || this.sourceModule[index] == null)
            {
                throw new Exception("no module");
            }
            return this.sourceModule[index];
        }

        public int GetSourceModuleCount()
        {
            return this.sourceModuleCount;
        }

        public virtual double GetValue(double x, double y, double z, double lowerXBound, double upperXBound, double lowerZBound, double upperZBound)
        {
            return 0.0;
        }

        public double GetValueInternal(int sourceIndex, double x, double y, double z, double lowerXBound, double upperXBound, double lowerZBound, double upperZBound)
        {
            this.fillSourceCache(lowerXBound, upperXBound, lowerZBound, upperZBound);

            if (this.cacheSource[sourceIndex])
            {
                int coordX = (int)Math.Floor(noisegen.MakeInt32Range(255.0 / (upperXBound - lowerXBound) * (x - lowerXBound)));
                int coordY = (int)Math.Floor(noisegen.MakeInt32Range(255.0 / (upperZBound - lowerZBound) * (z - lowerZBound)));

                coordX = Math.Max(0, coordX);
                coordX = Math.Min(254, coordX);
                coordY = Math.Max(0, coordY);
                coordY = Math.Min(254, coordY);

                return this.sourceCache[sourceIndex, coordX, coordY];
            }
            else
            {
                return this.sourceModule[sourceIndex].GetValue(x, y, z, lowerXBound, upperXBound, lowerZBound, upperZBound);
            }
        }

        public virtual void SetSourceModule(int index, Module sourceModule)
        {
            ////////TODO: assert(this.pSourceModule != null);
            if (index >= this.GetSourceModuleCount() || index < 0)
            {
                throw new Exception("invalid params");
            }
            this.sourceModule[index] = sourceModule;
            this.resetSourceCache[index] = true;
        }

        public virtual void SetParameter(string name, object value)
        {
        }

        public virtual object GetParameter(string name)
        {
            return null;
        }

        private void fillSourceCache(double lowerXBound, double upperXBound, double lowerZBound, double upperZBound)
        {
            for (int i = 0; i < this.sourceModuleCount; i++)
            {
                if (this.cacheSource[i] && this.resetSourceCache[i])
                {
                    int coordX = 0;
                    int coordY = 0;
                    double width = upperXBound - lowerXBound;
                    double height = upperZBound - lowerZBound;
                    double stepX = width / 255.0;
                    double stepZ = height / 255.0;

                    for (double x = lowerXBound; x < upperXBound - stepX; x += stepX)
                    {
                        coordY = 0;
                        for (double z = lowerZBound; z < upperZBound - stepZ; z += stepZ)
                        {
                            this.sourceCache[i, coordX, coordY] = this.sourceModule[i].GetValue(x, 0.0, z, lowerXBound, upperXBound, lowerZBound, upperZBound);
                            coordY++;
                        }
                        coordX++;
                    }

                    this.resetSourceCache[i] = false;
                }
            }
        }
    }
}
